﻿using System;
using GeoAPI.Geometries;
using NetTopologySuite.Algorithm;
using NetTopologySuite.Geometries;

namespace NetTopologySuite.Operation.Buffer
{
	public class BufferInputLineSimplifier
	{
		public static Coordinate[] Simplify(Coordinate[] inputLine, double distanceTol)
		{
			return new BufferInputLineSimplifier(inputLine).Simplify(distanceTol);
		}

		public BufferInputLineSimplifier(Coordinate[] inputLine)
		{
			this._inputLine = inputLine;
		}

		public Coordinate[] Simplify(double distanceTol)
		{
			this._distanceTol = System.Math.Abs(distanceTol);
			if (distanceTol < 0.0)
			{
				this._angleOrientation = -1;
			}
			this._isDeleted = new byte[this._inputLine.Length];
			while (this.DeleteShallowConcavities())
			{
			}
			return this.CollapseLine();
		}

		private bool DeleteShallowConcavities()
		{
			int num = 1;
			int num2 = this.FindNextNonDeletedIndex(num);
			int i = this.FindNextNonDeletedIndex(num2);
			bool result = false;
			while (i < this._inputLine.Length)
			{
				bool flag = false;
				if (this.IsDeletable(num, num2, i, this._distanceTol))
				{
					this._isDeleted[num2] = 1;
					flag = true;
					result = true;
				}
				if (flag)
				{
					num = i;
				}
				else
				{
					num = num2;
				}
				num2 = this.FindNextNonDeletedIndex(num);
				i = this.FindNextNonDeletedIndex(num2);
			}
			return result;
		}

		private int FindNextNonDeletedIndex(int index)
		{
			int num = index + 1;
			while (num < this._inputLine.Length - 1 && this._isDeleted[num] == 1)
			{
				num++;
			}
			return num;
		}

		private Coordinate[] CollapseLine()
		{
			CoordinateList coordinateList = new CoordinateList();
			for (int i = 0; i < this._inputLine.Length; i++)
			{
				if (this._isDeleted[i] != 1)
				{
					coordinateList.Add(this._inputLine[i]);
				}
			}
			return coordinateList.ToCoordinateArray();
		}

		private bool IsDeletable(int i0, int i1, int i2, double distanceTol)
		{
			Coordinate p = this._inputLine[i0];
			Coordinate coordinate = this._inputLine[i1];
			Coordinate p2 = this._inputLine[i2];
			return this.IsConcave(p, coordinate, p2) && BufferInputLineSimplifier.IsShallow(p, coordinate, p2, distanceTol) && this.IsShallowSampled(p, coordinate, i0, i2, distanceTol);
		}

		private bool IsShallowSampled(Coordinate p0, Coordinate p2, int i0, int i2, double distanceTol)
		{
			int num = (i2 - i0) / 10;
			if (num <= 0)
			{
				num = 1;
			}
			for (int j = i0; j < i2; j += num)
			{
				if (!BufferInputLineSimplifier.IsShallow(p0, p2, this._inputLine[j], distanceTol))
				{
					return false;
				}
			}
			return true;
		}

		private static bool IsShallow(Coordinate p0, Coordinate p1, Coordinate p2, double distanceTol)
		{
			return CGAlgorithms.DistancePointLine(p1, p0, p2) < distanceTol;
		}

		private bool IsConcave(Coordinate p0, Coordinate p1, Coordinate p2)
		{
			return CGAlgorithms.ComputeOrientation(p0, p1, p2) == this._angleOrientation;
		}

		private const int NumPtsToCheck = 10;

		private const int Delete = 1;

		private readonly Coordinate[] _inputLine;

		private double _distanceTol;

		private byte[] _isDeleted;

		private int _angleOrientation = 1;
	}
}
